package pao.akka.arithmetic;

import akka.actor.ActorSelection;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import pao.akka.util.Fitness;
import pao.akka.util.GBestMsg;
import pao.akka.util.PBestMsg;
import pao.akka.util.PsoValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @Author: 陶鹏飞
 * @Description:
 * @Date: 2018/9/10-21:38
 * @email: taopengfei1223@foxmail.com
 **/
public class Bird extends UntypedActor {

    private final LoggingAdapter loggingAdapter = Logging.getLogger(getContext().system(),this);
    private PsoValue pBest = null;
    private PsoValue gBest = null;
    private List<Double> velocity = new ArrayList<Double>(5);
    private List<Double> x = new ArrayList<Double>(5);
    private Random random = new Random();

    @Override
    public void preStart() throws Exception {
        for (int i = 0; i < 5; i++) {
            velocity.add(Double.NEGATIVE_INFINITY);
            x.add(Double.NEGATIVE_INFINITY);
        }

        // x1 <= 400
        x.set(1, (double) random.nextInt(401));

        //x2 <= 44 - 1.1 * x1
        double max = 400 - 1.1 * x.get(1);
        if (max < 0) {
            max = 0;
        }
        x.set(2, random.nextDouble() * max);

        //x3 <= 484 - 1.21 * x1 - 1.1 * x2
        max = 484 - 1.21 * x.get(1) - 1.1 * x.get(2);
        if (max < 0) {
            max = 0;
        }
        x.set(3,random.nextDouble() * max);

        //x4 <= 532.4 - 1.331 * x1 - 1.21 * x2 - 1.1 * x3
        max = 532.4 - 1.331 * x.get(1) - 1.21 * x.get(2) - 1.1 * x.get(3);
        if (max < 0) {
            max = 0;
        }
        x.set(4,random.nextDouble() * max);

        double newFir = Fitness.fitness(x);
        pBest = new PsoValue(newFir,x);
        PBestMsg pBestMsg = new PBestMsg(pBest);
        ActorSelection actorSelection = getContext().actorSelection("/user/masterbird");
        actorSelection.tell(pBestMsg,getSelf());
    }

    @Override
    public void onReceive(Object o) throws Exception {
        if (o instanceof GBestMsg) {
            gBest = ((GBestMsg) o).getPsoValue();
            //更新进度
            for (int i = 1; i < velocity.size(); i++) {
                updateVelocity(i);
            }

            //更新位置
            for (int i = 1; i < x.size(); i++) {
                updateX(i);
            }
            validateX();
            double newFit = Fitness.fitness(x);
            if (newFit > pBest.getValue()) {
                pBest = new PsoValue(newFit,x);
                PBestMsg pBestMsg = new PBestMsg(pBest);
                getSender().tell(pBestMsg,getSelf());
            }
        } else {
            unhandled(o);
        }
    }

    public void validateX() {
        if (x.get(1) > 400) {
            x.set(1, (double) random.nextInt(1));
        }

        //x2
        double max = 400 - 1.1 * x.get(1);
        if (x.get(2) > max || x.get(2) < 0) {
            x.set(2, random.nextDouble() * max);
        }

        //x3
        max = 484 - 1.21 * x.get(1) - 1.1 * x.get(2);
        if (x.get(3) > max || x.get(3) < 0) {
            x.set(3,random.nextDouble() * max);
        }

        //x4
        max = 532.4 - 1.331 * x.get(1) - 1.21 * x.get(2) - 1.1 * x.get(3);
        if (x.get(4) > max || x.get(4) < 0) {
            x.set(4, random.nextDouble() * max);
        }
    }

    public double updateX(int i) {
        double newX = x.get(i) + velocity.get(i);
        x.set(i,newX);
        return newX;
    }

    public double updateVelocity(int i) {
        double v = Math.random() * velocity.get(i)
                + 2 * Math.random() * (pBest.getX().get(i) - x.get(i))
                + 2 * Math.random() * (gBest.getX().get(i) - x.get(i));

        v = v > 0 ? Math.min(v,5) : Math.max(v,-5);
        velocity.set(i,v);

        return v;
    }
}
